use spades_lib::ai::*;
use spades_lib::player::*;
use spades_lib::round::Round;
use spades_lib::table::*;
use crossterm::event::{Event, KeyCode};
use log::info;
use std::any::Any;
use tge::{
    context::Context,
    event::{event_check, event_emit, timer_cancel, timer_fire, timer_register},
    game::Model,
};

pub const CARDW: usize = 7;
pub const CARDH: usize = 5;

pub enum SpadesState {
    Dealing,
    Bidding,
    Playing,
    Billing,
}

impl From<u8> for SpadesState {
    fn from(orig: u8) -> Self {
        let cs;
        match orig {
            0 => cs = SpadesState::Dealing,
            1 => cs = SpadesState::Bidding,
            2 => cs = SpadesState::Playing,
            3 => cs = SpadesState::Billing,
            _ => cs = SpadesState::Playing,
        };
        cs
    }
}

pub struct SpadesModel {
    pub players: [Player; 4],
    pub round: Round,
    pub table: Table,
}

impl SpadesModel {
    pub fn new() -> Self {
        let ps: [Player; 4] = [
            Player::new("ME", 0),
            Player::new("P1", 1),
            Player::new("P2", 2),
            Player::new("P3", 3),
        ];
        Self {
            players: ps,
            round: Round::new(),
            table: Table::new(),
        }
    }

    pub fn reset(&mut self, ctx: &mut Context) {
        timer_register("dealing", 0.2, "_");
        timer_register("bidding", 0.5, "_");
        timer_register("playing", 2.5, "_");

        ctx.rand.srand_now();
        self.round.reset(&mut ctx.rand);
        self.table.reset();
        ctx.state = SpadesState::Dealing as u8;

        timer_fire("dealing", 0);
    }

    pub fn dealing(&mut self, ctx: &mut Context) {
        self.round.deal(&mut ctx.rand, self.table.joker==1);
        ctx.state = SpadesState::Bidding as u8;
        if self.table.frist_banker == -1 {
            self.table.frist_banker = self.round.banker;
        }
        timer_fire("bidding", 0);
        info!("dealing ok, begin bidding...");
    }

    pub fn bidding(&mut self, ctx: &mut Context) {
        let tid = self.round.current_seat_id;
        let cseat = &mut self.round.seats[tid];
        // 如果手工输入，应用输入值，否则应用AI算法
        let bid = if cseat.bid != None {
            cseat.bid.unwrap()
        } else {
            ai_bid(tid, &mut self.table, &mut self.round)
        };
        info!("state bidding -> {:?}", bid);
        if self.round.bid(bid) {
            ctx.state = SpadesState::Playing as u8;
            timer_fire("playing", 0);
            info!("state bidding -> playing...");
        } else {
            timer_fire("bidding", 0);
        }
    }

    pub fn playing(&mut self, ctx: &mut Context) {
        let tid = self.round.current_seat_id;
        let cseat = &mut self.round.seats[tid];
        // 如果手工输入，应用输入值，否则应用AI算法
        let out_card = if cseat.out_card != None {
            cseat.out_card.unwrap()
        } else {
            ai_play(tid, &mut self.table, &mut self.round)
        };
        if self.round.play(out_card) {
            // round 结算 会更新table的 game_state
            self.round.compute_round_result(&mut self.table);
            //检查是否对局结束
            if self.table.check_game_finish() {
                let winner = self.table.winner_team_id;
                info!("game finish winner team -> {:}", winner);
            }
            //结算
            ctx.state = SpadesState::Billing as u8;
            timer_fire("billing", 0);
            info!("state playing -> billing...");
        } else {
            timer_fire("playing", 0);
        }
    }

    pub fn timer_process(&mut self, ctx: &mut Context) {
        let mut need_redraw = false;
        if event_check("dealing", "_") {
            self.dealing(ctx);
            need_redraw = true;
        }
        if event_check("bidding", "_") {
            self.bidding(ctx);
            need_redraw = true;
        }
        if event_check("playing", "_") {
            self.playing(ctx);
            need_redraw = true;
        }
        if need_redraw {
            event_emit("Spades.RedrawTurn");
            event_emit("Spades.RedrawTile");
            event_emit("Spades.RedrawMsg");
        }
    }
}

impl Model for SpadesModel {
    fn init(&mut self, ctx: &mut Context) {
        self.reset(ctx);
    }

    fn handle_input(&mut self, ctx: &mut Context, _dt: f32) {
        let es = ctx.input_events.clone();
        let cround = &mut self.round;
        let current_seat_id = cround.current_seat_id;
        for e in &es {
            if self.players[current_seat_id].is_robot() {
                continue;
            }
            match e {
                Event::Key(key) => {
                    let bn: Option<u8> = match key.code {
                        KeyCode::Char('0') => Some(0),
                        KeyCode::Char('1') => Some(1),
                        KeyCode::Char('2') => Some(2),
                        KeyCode::Char('3') => Some(3),
                        KeyCode::Char('4') => Some(4),
                        KeyCode::Char('5') => Some(5),
                        KeyCode::Char('6') => Some(6),
                        KeyCode::Char('7') => Some(7),
                        KeyCode::Char('8') => Some(8),
                        KeyCode::Char('9') => Some(9),
                        KeyCode::Char('a') => Some(10),
                        KeyCode::Char('b') => Some(11),
                        KeyCode::Char('c') => Some(12),
                        KeyCode::Char('d') => Some(13),
                        _ => None,
                    };
                    if bn == None {
                        continue;
                    }
                    let cseat = &mut cround.seats[current_seat_id];
                    match ctx.state.into() {
                        SpadesState::Bidding => {
                            cseat.bid = Some(bn.unwrap() as i16);
                            info!("me bid : {}", bn.unwrap());
                            timer_cancel("bidding", false);
                        }
                        SpadesState::Playing => {
                            let oidx = bn.unwrap() as usize - 1;
                            if oidx < cseat.cards_can.len() {
                                cseat.out_card = Some(cseat.cards_can.cards[oidx]);
                            }
                            info!("me out: {}", bn.unwrap());
                            timer_cancel("playing", false);
                        }
                        SpadesState::Billing => {}
                        SpadesState::Dealing => {}
                    }
                }
                _ => {}
            }
        }
        ctx.input_events.clear();
    }

    fn handle_timer(&mut self, ctx: &mut Context, _dt: f32) {
        self.timer_process(ctx);
    }

    fn handle_auto(&mut self, _ctx: &mut Context, _dt: f32) {}
    fn handle_event(&mut self, _ctx: &mut Context, _dt: f32) {}
    fn as_any(&mut self) -> &mut dyn Any {
        self
    }
}
